package com.qingzhuge.controller.base;

import com.google.common.collect.Lists;
import com.qingzhuge.common.annotation.ASysLogMethod;
import com.qingzhuge.common.annotation.SysLogTypeEnum;
import com.qingzhuge.common.enums.EAuthorityType;
import com.qingzhuge.common.enums.EResourceType;
import com.qingzhuge.common.validator.AddGroup;
import com.qingzhuge.common.validator.UpdateGroup;
import com.qingzhuge.common.validator.ValidatorUtil;
import com.qingzhuge.core.annotation.AResource;
import com.qingzhuge.dto.request.BaseRequestPkDto;
import com.qingzhuge.dto.response.BaseResponsePkDto;
import com.qingzhuge.dto.response.ResponseBodyDto;
import com.qingzhuge.entity.BaseBean;
import com.qingzhuge.entity.BaseModel;
import com.qingzhuge.service.IBaseService;
import com.qingzhuge.utils.PageUtil;
import io.swagger.annotations.ApiOperation;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import static com.qingzhuge.common.Constants.Controller.PATH_BY_ID;
import static com.qingzhuge.common.Constants.Controller.PATH_PAGE;

/**
 * @author : zero.xiao
 * @description : 控制器基类
 * @modified :
 */
public abstract class BasePkSerializableController<
        ID extends Serializable,
        T extends BaseBean<ID>,
        S extends IBaseService<T, ID>,
        ReqD extends BaseRequestPkDto<ID>,
        ResD extends BaseResponsePkDto<ID>> extends AbstractController {
    private Class<ResD> resDtoClass;
    protected S service;

    public BasePkSerializableController() {
        //泛型类作为父类，可以获取子类的所有实际参数的类型
        // 得到泛型父类
        Type genType = this.getClass().getGenericSuperclass();
        //一个泛型类可能有多个泛型形参，比如ClassName<T,K> 这里有两个泛型形参T和K，Class Name<T> 这里只有1个泛型形参T
        if (genType instanceof ParameterizedType) {
            Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
            int let = 3;
            if (params.length - 1 >= let) {
                Type param = params[let];
                resDtoClass = (Class<ResD>) param;
            }
        }
    }

    @Resource
    public void setService(S service) {
        this.service = service;
    }

    @ApiOperation(value = "分页")
    @PutMapping(PATH_PAGE)
    @AResource(type = EResourceType.button, authority = EAuthorityType.READ, name = "分页")
    @PreAuthorize("hasRole('ROOT') or hasAnyAuthority('SYSTEM','ADMIN') and hasAuthority('READ')")
    public ResponseEntity<ResponseBodyDto<PageUtil<ResD>>> queryPage(@RequestBody(required = false) ReqD requestDto) {
        T entity = service.dto2Bean(requestDto);
        return doQueryPage(entity);
    }

    protected ResponseEntity<ResponseBodyDto<PageUtil<ResD>>> doQueryPage(T entity) {
        PageUtil<T> page = service.query(entity);
        return returnSuccess(page,resDtoClass);
    }

    @ApiOperation(value = "列表")
    @AResource(type = EResourceType.button, authority = EAuthorityType.READ, name = "列表")
    public ResponseEntity<ResponseBodyDto<List<ResD>>> queryList(@RequestParam(required = false) ReqD requestDto) {
        T entity = service.dto2Bean(requestDto);
        return doQueryList(entity);
    }

    private ResponseEntity<ResponseBodyDto<List<ResD>>> doQueryList(T entity) {
        List<T> list = service.findAll(entity);
        List<ResD> resList = Lists.newArrayList();
        list.forEach(t -> resList.add(service.bean2Dto(t, resDtoClass)));
        return returnSuccess(resList);
    }

    @ApiOperation(value = "详情")
    @GetMapping(PATH_BY_ID)
    @AResource(type = EResourceType.button, authority = EAuthorityType.DETAIL)
    @PreAuthorize("hasRole('ROOT') or hasAnyAuthority('SYSTEM','ADMIN') and hasAuthority('DETAIL')")
    public ResponseEntity<ResponseBodyDto<ResD>> get(@PathVariable ID id) {
        return this.doGet(id);
    }

    private ResponseEntity<ResponseBodyDto<ResD>> doGet(ID id) {
        T result = service.findById(id);
        return returnSuccess(service.bean2Dto(result, resDtoClass));
    }

    @AResource(type = EResourceType.button, authority = EAuthorityType.ADD)
    @ApiOperation(value = "新建")
    @ASysLogMethod(type = SysLogTypeEnum.ADD,value = "新建")
    @PostMapping
    @PreAuthorize("hasRole('ROOT') or hasAnyAuthority('SYSTEM','ADMIN') and hasAuthority('SAVE')")
    public ResponseEntity<ResponseBodyDto<ResD>> save(@RequestBody ReqD requestDto) {
        T entity = service.dto2Bean(requestDto);
        return doUpdate(entity);
    }

    @AResource(type = EResourceType.button, authority = EAuthorityType.UPDATE)
    @ApiOperation(value = "修改")
    @ASysLogMethod(type = SysLogTypeEnum.UPDATED,value = "修改")
    @PutMapping
    @PreAuthorize("hasRole('ROOT') or hasAnyAuthority('SYSTEM','ADMIN') and hasAuthority('UPDATE')")
    public ResponseEntity<ResponseBodyDto<ResD>> update(@RequestBody ReqD requestDto) {
        T entity = service.dto2Bean(requestDto);
        return doUpdate(entity);
    }

    private ResponseEntity<ResponseBodyDto<ResD>> doUpdate(T entity) {
        Long userId = getUid();
        if (entity instanceof BaseModel) {
            BaseModel bean = (BaseModel) entity;
            if (entity.isNew()) {
                ValidatorUtil.validateEntity(entity, AddGroup.class);
                bean.setCreateBy(userId);
            } else {
                ValidatorUtil.validateEntity(entity, UpdateGroup.class);
                bean.setUpdateBy(userId);
            }
        }
        entity = service.save(entity);
        ResD res = service.bean2Dto(entity, resDtoClass);
        return returnSuccess(res);
    }

    /**
     * 删除
     */
    @ApiOperation(value = "删除")
    @ASysLogMethod(type = SysLogTypeEnum.DEL,value = "删除")
    @AResource(type = EResourceType.button, authority = EAuthorityType.DELETE)
    @DeleteMapping(PATH_BY_ID)
    @PreAuthorize("hasRole('ROOT') or hasAnyAuthority('SYSTEM','ADMIN') and hasAuthority('DELETE')")
    public ResponseEntity<ResponseBodyDto<Object>> delete(@PathVariable ID id) {
        return this.doDelete(id);
    }

    /**
     * 删除
     */
    private ResponseEntity<ResponseBodyDto<Object>> doDelete(ID id) {
        service.deleteById(id);
        return returnSuccess();
    }
}
